package cs6238.client;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.util.UUID;

import cs6238.common.CertificateHelper;
import cs6238.common.Communication;
import cs6238.common.Configuration;
import cs6238.common.DelegateEntry;
import cs6238.common.FileInfo;
import cs6238.common.message.command.DelegateCommand;
import cs6238.common.message.command.GetFileCommand;
import cs6238.common.message.command.ListHostsCommand;
import cs6238.common.message.command.LsCommand;
import cs6238.common.message.command.PutFileCommand;
import cs6238.common.message.response.ListHostsResponse;
import cs6238.common.message.response.LsResponse;
import cs6238.common.message.response.StandardResponse;

public class ClientCommunication extends Communication {


	private PrintStream _out;
	
	public ClientCommunication(Socket socket) throws IOException{
		super(socket);
		
		_out = System.out;
	}
	
	//This should only be used for unit testing
	protected ClientCommunication(InputStream input, OutputStream output) throws IOException {
		super(input, output);
		_out = System.out;
	}
	
	public void setOut(PrintStream out){
		_out = out;
	}

	public void startSession(String hostname){
		
	}
	
	public void executeCommand(LsCommand lsCommand) throws IOException {
		this.sendCommand(new LsCommand());
		LsResponse response = (LsResponse)this.receiveResponse();
		
		for(FileInfo info : response.getFileInfoList()){
			_out.println("\t" + info.toString());
		}
	}
	
	public UUID getFileIdForFilename(String filename) throws IOException{
		this.sendCommand(new LsCommand());
		LsResponse response = (LsResponse)this.receiveResponse();
		
		for(FileInfo info : response.getFileInfoList()){
			if(info.getFilename().equals(filename)){
				return info.getFileId();
			}
		}
		
		return null;
	}

	public void executeCommand(GetFileCommand getCommand, String filename) throws IOException {
		this.sendCommand(getCommand);
		StandardResponse response = (StandardResponse)this.receiveResponse();
		
		if(response.isOk()){
			this.receiveFileAndSaveToDisk(filename);
		}
		else{
			_out.println("Received invalid response");
		}
	}


	public void executeCommand(PutFileCommand putCommand) throws IOException {
		if(!new File(putCommand.getClientFilename()).exists()){
			_out.println("File does not exist");
			return;
		}
		
		this.sendCommand(putCommand);
		StandardResponse response = (StandardResponse)this.receiveResponse();
		
		if(response.isOk()){
			this.sendFile(putCommand.getClientFilename());
			
			StandardResponse response2 = (StandardResponse)this.receiveResponse();
			
			if(response2.isOk()){
				_out.println("File successfully uploaded");
			}
			else{
				_out.println("Error uploading file");
			}
			
		}
		else{
			_out.println("Received invalid response");
		}
	}


	public void executeCommand(DelegateCommand delegateCommand) throws IOException {
		signDelegateEntry(delegateCommand.getDelegateEntry());
		this.sendCommand(delegateCommand);
		
		StandardResponse response2 = (StandardResponse)this.receiveResponse();
		
		if(response2.isOk()){
			_out.println("Delegation successful");
		}
		else{
			_out.println("Received invalid response");
		}
	}
	
	public void signDelegateEntry(DelegateEntry entry){
		try{
			Configuration clientConfig = Configuration.loadClientSettings();
			String filepath = clientConfig.getSetting(Configuration.KEYSTORE_PATH);
			String password = clientConfig.getSetting(Configuration.KEYSTORE_PASSWORD);
			KeyStore store = CertificateHelper.readJKS(filepath, password);
			PrivateKey privateKey = (PrivateKey)store.getKey(CertificateHelper.ALIAS, password.toCharArray());
			
			byte[] message = entry.serializeWithoutSignature();
			byte[] signature = CertificateHelper.signMessage(message, privateKey);
			
			entry.setSignature(signature);	
		}catch(Exception e){
			_out.println("Error signing DelegateEntry");
			e.printStackTrace();
		}
	}
	
	public void executeCommand(ListHostsCommand listHostsCommand) throws IOException{
		this.sendCommand(listHostsCommand);
		ListHostsResponse response = (ListHostsResponse)this.receiveResponse();
		
		_out.println("Available Hosts:");
		for(String host : response.getHosts()){
			_out.println("\t" + host);
		}
	}
}